#ifndef __HTTP_SERVER_HPP__
#define __HTTP_SERVER_HPP__

#include "../src/Server.hpp"
#include "Http.hpp"

/**
 * @brief 高性能(支持高并发)的HTTP服务器
 * 
 */
class HttpServer
{
    using HandleFunc = std::function<void(const HttpRequest&, HttpResponse*)>;// 请求的回调函数类型

    // using Route_t = std::unordered_map<std::string, HandleFunc>;// 请求路由表 <表示请求资源的正则表达式, 对应处理函数>
    // 编译正则表达式比较费时间，所以只编译一次就够了
    // using Route_t = std::unordered_map<std::regex, HandleFunc>;// 请求路由表 <编译好的正则表达式匹配规则, 对应处理函数>

    using Route_t = std::vector<std::pair<std::regex, HandleFunc>>;// 请求路由表 pair<编译好的正则表达式匹配规则, 对应处理函数>，键码表示的匹配规则匹配的是请求资源路径，键值表示的是请求该类请求资源的业务回调函数
private:
    TcpServer _server;              // 高性能TCP服务器
    // size_t _threadPoolSize;         // 线程池大小
    std::string _rootPath;          // 静态资源的相对根目录
    Route_t _getRoute;              // GET请求的路由表
    Route_t _postRoute;             // POST请求的路由表
    Route_t _headRoute;             // HEAD请求的路由表
    Route_t _deleteRoute;           // DELETE请求的路由表
    Route_t _putRoute;              // PUT请求的路由表
private:
    /**
     * @brief 构建错误情况的HTTP响应。只填充错误页面到响应正文
     * 
     * @param resp HttpResponse* 要构建的响应
     * @param statusCode 错误码
     */
    void ErrorResponse(HttpResponse* resp, int statusCode)
    {
#if 0
        /* 构建错误页面 */
        std::string errorCodeStr = std::to_string(statusCode);
        std::string errorDescription = Utils::StatusCodeDescrib(statusCode);
        std::string errorPage;
        if(Utils::ReadFile("www/error.html", &errorPage) == false)
        {
            errorPage.clear();
            errorPage += "<html lang='en'>";
            errorPage += "<head>";
            errorPage += "<meta name='viewport' content='width=device-width, initial-scale=1.0' charset='UTF-8'>";
            errorPage += "</head>";
            errorPage += "<body>";
            errorPage += "<h1>";
            errorPage += errorCodeStr;
            errorPage += "h1";
            errorPage += "<p>";
            errorPage += errorDescription;
            errorPage += "</p>";
            errorPage += "</body>";
            errorPage += "</html>";
        }
        else
        {
            
            std::string errorCodePlaceholder = "{{error_code}}";
            std::string errorDescriptionPlaceholder = "{{error_description}}";

            size_t pos = 0;
            while ((pos = errorPage.find(errorCodePlaceholder, pos)) != std::string::npos) 
            {
                errorPage.replace(pos, errorCodePlaceholder.length(), errorCodeStr);
                pos += errorCodeStr.length();
            }

            pos = 0;
            while ((pos = errorPage.find(errorDescriptionPlaceholder, pos)) != std::string::npos) 
            {
                errorPage.replace(pos, errorDescriptionPlaceholder.length(), errorDescription);
                pos += errorDescription.length();
            }
        }
#endif
        std::string errorPage;
        errorPage += "<html lang='en'>";
        errorPage += "<head>";
        errorPage += "<meta name='viewport' content='width=device-width, initial-scale=1.0' charset='UTF-8'>";
        errorPage += "</head>";
        errorPage += "<body>";
        errorPage += "<h1>";
        errorPage += std::to_string(statusCode);
        errorPage += "</h1>";
        errorPage += "<p>";
        errorPage += Utils::StatusCodeDescrib(statusCode);
        errorPage += "</p>";
        errorPage += "</body>";
        errorPage += "</html>";
        /* 发送 */
        resp->SetContent(errorPage);// "text/html"
    }
    /**
     * @brief 功能路由匹配。根据请求信息，在相应请求路由表中寻找相匹配的业务回调函数，找到了就执行；
     * 找不到就填充相关错误信息到HttpResponse对象
     * 
     * @param req  输入输出型参数 请求
     * @param resp 输出型参数 返回的响应
     * @param callback 输入型参数 请求对应的回调函数
     */
    void RouteDispatcher(HttpRequest& req, HttpResponse* resp, const Route_t& routeTable)
    {
        // DBG_LOG("REQUEST METHOD: %s", req._method.c_str());
        // DBG_LOG("REQUEST PATH: %s", req._path.c_str());
        // DBG_LOG("REQUEST VERSION: %s", req._version.c_str());

        for(const auto& pair : routeTable)
        {
            if(std::regex_match(req._path, req._matchers, pair.first)) return pair.second(req, resp);
        }

        /* 功能请求资源不存在 */
        std::string head = req._method + " " + req._path + " " + req._version;
        ERR_LOG("NO MATCHED HANDLER FOR REQUEST: %s", head.c_str());
        return ErrorResponse(resp, 404);
    }
    /**
     * @brief 判断一个请求是否是静态资源请求
     * 
     * @param req 输入型参数 HttpRequest
     * @param resourcePath 输出型参数请求资源的完整路径
     * @return true 是；\
     * @return false 不是
     */
    bool IsStaticResourceRequest(const HttpRequest& req, std::string* resourcePath) const
    {
        /* 1. 存在静态资源相对根目录 */
        if(_rootPath.empty()) return false;
        /* 2. 请求方法是GET或HEAD */
        if(req._method != "GET" && req._method != "POST") return false;
        /* 3. 请求资源路径合法 */
        if(Utils::ValidPath(req._path) == false) return false;
        /* 4. 请求资源存在，且是普通文件 */ 
        *resourcePath = _rootPath;// 请求资源完整路径
        if(req._path.empty()) *resourcePath + "/index.html";// 相对路径为空
        else if(req._path.back() == '/') *resourcePath += "/index.html";// 相对路径为目录
        else *resourcePath += req._path;

        if(Utils::IsRegular(*resourcePath) == false) return false;

        // 是静态资源请求
        return true;
    }
    /**
     * @brief 路由匹配+业务处理。根据请求req中的信息，进行静态路由或功能路由，
     * 如果是静态路由，则获取相关静态资源，填充到响应resp中；
     * 如果是功能路由，则在路由表中进行路由匹配，查找得到相应的业务回调函数，成功则执行，并将结果填充到resp中；
     * 若静态路由查找的资源不存在，则返回相应错误响应信息；
     * 若功能路由的相匹配的业务回调函数不存在，则返回相应错误响应信息
     * 
     * @param conn 输入型参数 用户连接
     * @param req 输入输出型参数，是HTTP请求信息
     * @param resp 输出型参数，是要返回的响应信息
     */
    void Route(const ConnectionPtr& conn, HttpRequest& req, HttpResponse* resp)
    {
        /* 1. 静态资源请求 先默认GET和POST请求是静态资源请求 */
        std::string resourcePath;
        if(IsStaticResourceRequest(req, &resourcePath)) 
        {
            /* // 不需要再额外实例化一个string对象
            std::string resource;// 静态资源内容
            if(Utils::ReadFile(resourcePath, &resource) == false) 
                return ErrorResponse(resp, 507);// Insufficient Storage 读取静态资源失败
            resp->SetContent(resource, Utils::SufMime(resourcePath));
            */
            if(Utils::ReadFile(resourcePath, &resp->_body) == false) 
                return ErrorResponse(resp, 507);// Insufficient Storage 读取静态资源失败
            resp->SetHeader("Content-Type", Utils::SufMime(resourcePath));
            return;
        }

        /* 2. 功能请求 */
        if(req._method == "GET") return RouteDispatcher(req, resp, _getRoute);
        if(req._method == "POST") return RouteDispatcher(req, resp, _postRoute);
        if(req._method == "HEAD") return RouteDispatcher(req, resp, _headRoute);
        if(req._method == "DELETE") return RouteDispatcher(req, resp, _deleteRoute);
        if(req._method == "PUT") return RouteDispatcher(req, resp, _putRoute);

        return ErrorResponse(resp, 405);// Method Not Allowed
    }

    /**
     * @brief 构建并发送序列化HTTP响应数据。
     * 
     * @param resp 结构化HTTP响应
     */
    void BuildAndSendResp(const ConnectionPtr& conn, const HttpRequest& req, HttpResponse* resp)
    {
        /* 1. 填充头部字段 */
        // 长短连接
        if(req.ToClose()) resp->SetHeader("Connection", "close");
        else resp->SetHeader("Connection", "keep-alive");
        // 正文长度
        if(req.ContentLength() > 0 && resp->HasHeader("Content-Length") == false) 
            resp->SetHeader("Content-Length", std::to_string(req.ContentLength()));
        // 正文类型
        if(req.ContentLength() > 0 && resp->HasHeader("Content-Type") == false)
            resp->SetHeader("Content-Type", "application/octet-stream");// 默认二进制文件
        // 重定向
        if(resp->_redirect) resp->SetHeader("Location", resp->_redirectPath);
        /* 2. 构建序列化响应 */
        std::string seqResp;// 序列化响应
        /* HTTP协议版本就不在这里设置了，而是在HttpRequest初始化时设置
        if(req._version.empty()) seqResp += "HTTP/1.0";
        else seqResp += req._version;
        seqResp += " " + std::to_string(resp->_statusCode) + " " + Utils::StatusCodeDescrib(resp->_statusCode);// 响应行
        */
        seqResp += req._version + " " + std::to_string(resp->_statusCode) + " " + Utils::StatusCodeDescrib(resp->_statusCode) + "\r\n";// 响应行
        for(auto& pair : resp->_headers)// 响应头部
        {
            seqResp += pair.first + ": " + pair.second + "\r\n";
        }
        seqResp += "\r\n";// 空行
        seqResp += resp->_body;// 响应正文
        /* 3. 发送 */
        conn->Send(seqResp);
    }
private:
    /**
     * @brief 连接建立成功，为其设置支持的协议，即设置协议上下文。这里就是设置HTTP协议上下文
     * 
     * @param conn ConnectionPtr类型，用户连接
     */
    void OnConnected(const ConnectionPtr& conn)
    {
        INF_LOG("SERVER ACCEPT ONE NEW CONNECTION: %ld, SOCKET: %d", conn->Id(), conn->Fd());

        conn->SetContext(HttpContext());
    }
    /**
     * @brief 连接释放时调用的回调函数
     * 
     * @param conn ConnectionPtr类型，用户连接
     */
    void OnClosed(const ConnectionPtr& conn)
    {
        INF_LOG("CONNECTION: %d LISTING SOCKET: %ld HAS BEEN CLOSED", conn->Id(), conn->TmpFd());
    }
    /**
     * @brief 对连接的缓冲区中的数据进行获取并解析，并对解析得到的数据进行业务处理。
     * 
     * @param conn ConnectionPtr类型，用户连接
     * @param buffer 输入输出型参数 缓冲区
     */
    void OnMessage(const ConnectionPtr& conn, Buffer* buffer)/* Buffer*设置成指针，是为了和TcpServer适配 */
    {
        while(buffer->ReadAbleSize() > 0)/* 缓冲区中有数据，就进行处理 */
        {
            // DBG_LOG("recv before buffer's ReadAbleSize: %ld", buffer->ReadAbleSize());
            // DBG_LOG("\r\nbuffer: [\r\n%s]", buffer->ReadPosition());
            /* 1. 获取上下文 */
            HttpContext* context = conn->GetContext()->Get<HttpContext>();
            // DBG_LOG("parse before context recv status: %d", context->RecvStatus());
            // DBG_LOG("parse before context resp status: %d", context->RespStatusCode());
            /* 2. 根据上下文，按照规则对缓冲区中的数据进行解析，获取HttpRequst对象 */
            context->RecvHttpRequest(buffer);
            // DBG_LOG("recv after buffer's ReadAbleSize: %ld", buffer->ReadAbleSize());
            // DBG_LOG("parse after context recv status: %d", context->RecvStatus());
            // DBG_LOG("parse after context resp status: %d", context->RespStatusCode());
            HttpResponse resp;
            /* 2.1 解析出错，直接返回错误页面 */
            if(context->RespStatusCode() >= 400)
            {
                ErrorResponse(&resp, context->RespStatusCode());
                // DBG_LOG("ERROR RESPONSE, STATUSCODE: %d", context->RespStatusCode());
                // DBG_LOG("%s", resp._body.c_str());
                BuildAndSendResp(conn, context->Get(), &resp);
                /* conn->Close()关闭连接前，会处理缓冲区中的数据，若有数据，就会再次调用回调函数即OnMessage()，而RespStatusCode()
                仍然大于等于400，所以会再次进入该作用域，就造成死循环 */
                // 解决方法一：重置上下文
                context->Reset();
                // 解决方法二：清空缓冲区
                buffer->Clear();

                conn->Close();
                return;
            }

            /* 2.2 必须接收并解析完成，才能继续处理 请求不完整，直接退出，等待下次接收数据 */
            if(context->RecvStatus() != RECV_HTTP_SUCCESS) return;

            // 走到这里，请求接收并解析成功

            HttpRequest& req = context->Get();
            /* 3. 路由匹配 */
            Route(conn, req, &resp);
            /* 4. 构建序列化HTTP响应并发送 */
            BuildAndSendResp(conn, req, &resp);

            /* 5. 本次通信核心功能完成，重置协议上下文。保证当前连接上的下次通信不受本次通信的干扰 */
            context->Reset();

            /* 6. 根据连接性质：长短连接，判断是否继续处理，或直接关闭当前连接 */
            if(resp.ToClose())
            {
                conn->Close();
            }
        }
    }
public:
    /**
     * @brief 实例化一个HTTP服务器。
     * 默认启动连接超时关闭功能。
     * 
     * @param port 服务器监听的端口号
     * @param timeout 服务器管理的通信连接的默认超时时间
     */
    HttpServer(uint16_t port, size_t timeout = HTTP_SERVER_DEFAULT_TIMEOUT): _server(port)
    {
        _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
        _server.SetClosedCallback(std::bind(&HttpServer::OnClosed, this, std::placeholders::_1));
        _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));

        _server.EnableIdelRelease(timeout);// HTTP服务器应默认开启非活跃连接超时销毁功能
    }
    /**
     * @brief 设置静态资源的相对根路径
     * 
     * @param path 静态资源的相对根路径
     */
    void SetRootPath(const std::string& path)
    {
        assert(Utils::IsDir(path));
        _rootPath = path;
    }
#if 0 /* 不需要手动启动非活跃连接释放功能，默认开启 */
    /**
     * @brief 启动非活跃连接超时释放功能
     * 
     * @param timeout 超时时间
     */
    void EnableIdleRelease(uint32_t timeout)
    {
        _server.EnableIdelRelease(timeout);
    }
#endif
    /**
     * @brief 设置线程池支持的最大线程数
     * 
     * @param count 最大线程数
     */
    void SetThreadCount(size_t count)
    {
        _server.SetLoopThreadCount(count);
    }
    /**
     * @brief 开始监听
     * 
     */
    void Listen()
    {
        _server.Start();
    }

    /**
     * @brief 添加GET请求的功能路由
     * 
     * @param reqPath 请求资源路径
     * @param handler 对该请求资源的业务回调函数
     */
    void AddGetRoute(const std::string& reqPath, const HandleFunc& handler)
    {
        _getRoute.push_back(std::make_pair(std::regex(reqPath), handler));
    }
    /**
     * @brief 添加POST请求的功能路由
     * 
     * @param reqPath 请求资源路径
     * @param handler 对该请求资源的业务回调函数
     */
    void AddPostRoute(const std::string& reqPath, const HandleFunc& handler)
    {
        _postRoute.push_back(std::make_pair(std::regex(reqPath), handler));
    }
    /**
     * @brief 添加HEAD请求的功能路由
     * 
     * @param reqPath 请求资源路径
     * @param handler 对该请求资源的业务回调函数
     */
    void AddHeadRoute(const std::string& reqPath, const HandleFunc& handler)
    {
        _headRoute.push_back(std::make_pair(std::regex(reqPath), handler));
    }
    /**
     * @brief 添加DELETE请求的功能路由
     * 
     * @param reqPath 请求资源路径
     * @param handler 对该请求资源的业务回调函数
     */
    void AddDeleteRoute(const std::string& reqPath, const HandleFunc& handler)
    {
        _deleteRoute.push_back(std::make_pair(std::regex(reqPath), handler));
    }
    /**
     * @brief 添加PUT请求的功能路由
     * 
     * @param reqPath 请求资源路径
     * @param handler 对该请求资源的业务回调函数
     */
    void AddPutRoute(const std::string& reqPath, const HandleFunc& handler)
    {
        _putRoute.push_back(std::make_pair(std::regex(reqPath), handler));
    }
};

#endif